package org.nativejpa.parser;

import org.nativejpa.util.XMLUtils;

import javax.persistence.PersistenceException;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.Enumeration;
import java.net.URL;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;

/**
 * load & parse persistence.xml to Object
 *
 * @author <a href="mailto:jfox.young@gmail.com">Young Yang</a>
 * @create 2009-2-17 15:35:10
 */
public class PersistenceXML {
    static Log log = LogFactory.getLog(PersistenceXML.class);

    public static final String PERSISTENCE_XML_FILENAME = "META-INF/persistence.xml";

    public static final String NAME = "name";
    public static final String JTA_DATA_SOURCE = "jta-data-source";
    public static final String PERSISTENCE_UNIT = "persistence-unit";
    public static final String PROPERTY = "property";
    public static final String PROPERTY_NAME = "name";
    public static final String PROPERTY_VALUE = "value";

    public static final String DATASOURCE_JDBC_DRIVER = "datasource.jdbc.driver";
    public static final String DATASOURCE_JDBC_URL = "datasource.jdbc.url";
    public static final String DATASOURCE_JDBC_USERNAME = "datasource.jdbc.username";
    public static final String DATASOURCE_JDBC_PASSWORD = "datasource.jdbc.password";
    public static final String DATASOURCE_XA_DRIVER = "datasource.xa.driver";
    // to define XA Driver properties, ex: datasource.xa.driver.property.user corresponds with setUser
    public static final String DATASOURCE_XA_DRIVER_PROPERTY_PREFIX = "datasource.xa.driver.property.";
    public static final String DATASOURCE_POOL_MINSIZE = "datasource.pool.minsize";
    public static final String DATASOURCE_POOL_MAXSIZE = "datasource.pool.maxsize";
    public static final String DATASOURCE_POOL_MAX_IDLE = "datasource.pool.maxidle";
    public static final String DATASOURCE_POOL_TEST_QUERY = "datasource.pool.testquery";
    public static final String DATASOURCE_POOL_CONNECT_TIMEOUT = "datasource.pool.connecttimeout";

    public static final String CACHE_TYPE = "cache.type"; // local/memcached/memcached+local
    public static final String CACHE_TYPE_DISABLED = "disabled";
    public static final String CACHE_TYPE_LOCAL = "local";
    public static final String CACHE_TYPE_MEMCACHED = "memcached";
    public static final String CACHE_TYPE_MEMCACHED_LOCAL = "memcached+local";
    public static final String CACHE_TYPE_LOCAL_MEMCACHED = "local+memcached";
    public static final String CACHE_LOCAL_MAXSIZE = "cache.local.maxsize";
    public static final String CACHE_LOCAL_MAX_MEMORY_SIZE = "cache.local.max_memory_size";
    public static final String CACHE_LOCAL_EVICT_ALGORITHM = "cache.local.evict_algorithm";
    public static final String CACHE_LOCAL_EVICT_ALGORITHM_LRU = "LRU";
    public static final String CACHE_LOCAL_EVICT_ALGORITHM_LFU = "LFU";
    public static final String CACHE_LOCAL_EVICT_ALGORITHM_FIFO = "FIFO";
    public static final String CACHE_LOCAL_TTL = "cache.local.ttl";
    public static final String CACHE_LOCAL_MAX_IDLE_TIME = "cache.local.max_idle_time";
    public static final String CACHE_MEMCACHED_SERVERS = "cache.memcached.servers";
    public static final String CACHE_MEMCACHED_SERVERS_WEIGHTS = "cache.memcached.servers.weights";
    public static final String CACHE_MEMCACHED_POOL_INITCONN = "cache.memcached.pool.init_connection";
    public static final String CACHE_MEMCACHED_POOL_MINCONN = "cache.memcached.pool.min_connectoin";
    public static final String CACHE_MEMCACHED_POOL_MAXCONN = "cache.memcached.pool.max_connection";
    public static final String CACHE_MEMCACHED_POOL_MAINTSLEEP = "cache.memcached.pool.maintain_sleep";
    public static final String CACHE_MEMCACHED_SOCKET_NAGLE = "cache.memcached.socket.nagle";
    public static final String CACHE_MEMCACHED_SOCKET_TIMEOUT = "cache.memcached.socket.timeout";
    public static final String CACHE_MEMCACHED_SOCKET_CONNECTTIMEOUT = "cache.memcached.socket.connect_timeout";
    public static final String CACHE_MEMCACHED_COMPRESS_ENABLE = "cache.memcached.compress.enable";
    public static final String CACHE_MEMCACHED_COMPRESS_THRESHOLD = "cache.memcached.compress.threshold";
    // cache.memcached.hashingalgorithm=SockIOPool.NEW_COMPAT_HASH
    public static final String CACHE_MEMCACHED_HASHING_ALGORIGHM = "cache.memcached.hashing_algorithm";
    public static final String CACHE_MEMCACHED_SANITIZE_KEYS = "cache.memcached.sanitize_keys";
    public static final String CACHE_MEMCACHED_PRIMITIVE_AS_STRING = "cache.memcached.primitive_as_string";
    public static final String CACHE_MEMCACHED_FAILOVER = "cache.memcached.failover";
    public static final String CACHE_MEMCACHED_FAILBACK = "cache.memcached.failback";
    public static final String CACHE_MEMCACHED_TTL = "cache.memcached.ttl";
    public static final String CACHE_MEMCACHED_MAX_IDLE_TIME = "cache.memcached.max_idle_time";

    private Map<String, PersistenceUnit> persistenceUnitMap = new HashMap<String, PersistenceUnit>();

    public static PersistenceXML loadPersistenceXML(){
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> urls = null;
        try {
            urls = loader.getResources(PERSISTENCE_XML_FILENAME);
        }
        catch (IOException e) {
            throw new PersistenceException("Failed to load " + PERSISTENCE_XML_FILENAME, e);
        }
        if(!urls.hasMoreElements()) {
            throw new PersistenceException(new FileNotFoundException(PERSISTENCE_XML_FILENAME));
        }

        PersistenceXML persistenceXML = new PersistenceXML();
        while(urls.hasMoreElements()) {
            URL url = urls.nextElement();
            log.debug("Start to parse persistence.xml, " + url.toString());
            try {
                Document pxDocument = XMLUtils.loadDocument(url);
                List<Element> persistenceUnits = XMLUtils.getElementsByTagName(pxDocument.getDocumentElement(), PERSISTENCE_UNIT);

                for (Element element : persistenceUnits) {
                    String unitName = XMLUtils.getAtrributeValue(element, NAME);

                    if(persistenceXML.getPersistenceUnit(unitName) != null) {
                        throw new PersistenceException("Unit Name: " + unitName + " has been existed in " + persistenceXML.getPersistenceUnit(unitName).getPersistenceXML());
                    }

                    PersistenceUnit persistenceUnit = new PersistenceUnit(unitName, url);

                    List<Element> mappingFileElements = XMLUtils.getElementsByTagName(element, "mapping-file");
                    for(Element mappingFileElement : mappingFileElements){
                        persistenceUnit.addMappingFile(XMLUtils.getNodeValue(mappingFileElement));
                    }

//            String jndiName = "java:/" + unitName;
//            String jtaDataSource = XMLUtils.getChildElementValueByTagName(element, JTA_DATA_SOURCE);
//            if (jtaDataSource != null && !jtaDataSource.trim().equals("")) {
//                jndiName = jtaDataSource;
//            }

//                Map<String, String> properties = new HashMap<String, String>();
                    List<Element> propertysElements = XMLUtils.getElementsByTagName(element, PROPERTY);
                    for (Element propElement : propertysElements) {
                        persistenceUnit.addProperty(XMLUtils.getAtrributeValue(propElement, PROPERTY_NAME), XMLUtils.getAtrributeValue(propElement, PROPERTY_VALUE));
                    }
                    persistenceXML.persistenceUnitMap.put(unitName, persistenceUnit);
                }
                log.debug("Finished parsing persistence.xml, " + persistenceXML.toString());
            }
            catch (Exception e) {
                throw new PersistenceException("Failed to load: " + url.toString(), e);
            }
        }
        return persistenceXML;
    }

    public PersistenceUnit getPersistenceUnit(String unitName) {
        return persistenceUnitMap.get(unitName);
    }

    public Collection<PersistenceUnit> getAllPersistenceUnits() {
        return persistenceUnitMap.values();
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("{");
        int i = 0;
        for (PersistenceUnit persistenceUnit : getAllPersistenceUnits()) {
            if (i > 0) {
                sb.append(", ");
            }
            sb.append(persistenceUnit.getUnitName()).append(" = ").append(persistenceUnit.getAllProperties());
            i++;
        }
        sb.append("}");
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        PersistenceXML persistenceXML = PersistenceXML.loadPersistenceXML();
        System.out.println(persistenceXML);
    }
}
